/* * Copyright 2012 Jörg Hoh, Alexander Saar, Markus Haack * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.joerghoh.cq5.jmx.requests; import java.io.IOException; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Service; import org.apache.felix.scr.annotations.Component; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.wcm.api.Page; /** * RequestFilter -- provide information about requests * * @author joerg * * This service maintains a set of services to deliver information about * incoming requests. These services are then exported as MBeans via JMX * Whiteboard. */ @Component(immediate = true) @Service @Properties({ @Property(name = "sling.filter.scope", value = "request") }) public class RequestFilter implements Filter { private static String MBEAN_PREFIX = "de.joerghoh.cq5.jmx.requests"; private Logger log = LoggerFactory.getLogger(RequestFilter.class); private Map<String, ServiceRegistration> services = new HashMap<String, ServiceRegistration>(); private BundleContext bundleContext; private boolean shutdownInProgress = false; // livecycle stuff @Activate protected void activate(ComponentContext ctx) { bundleContext = ctx.getBundleContext(); shutdownInProgress = false; } @Deactivate protected void deactivate() { shutdownInProgress = true; for (ServiceRegistration sr : services.values()) { sr.unregister(); } } public void init(FilterConfig config) throws ServletException { } public void destroy() { } // implementation public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { RequestInformationImpl rii = null; if (!shutdownInProgress) { SlingHttpServletRequest req = (SlingHttpServletRequest) request; Resource r = req.getResource(); String contentType = req.getResponseContentType(); Page p = r.adaptTo(Page.class); String templatePath = ""; if (p != null && p.getTemplate() != null) { templatePath = p.getTemplate().getPath(); } else { // not a page or no template specified } // If there is no service defined for this mime type yet, we // register it String designator = buildMBeanName(contentType, templatePath); if (!services.containsKey(designator)) { registerReportingService(designator, contentType); } // TODO cache service objects for performance??? ServiceRegistration reg = services.get(designator); Object o = bundleContext.getService(reg.getReference()); if (o instanceof RequestInformationImpl) { rii = (RequestInformationImpl) o; } else { log.error("Something went wrong with the retrieval of the service object for meban " + designator); rii = null; } } // forward the request down the chain and collect timing information long t1 = System.currentTimeMillis(); chain.doFilter(request, response); long t2 = System.currentTimeMillis(); if (!shutdownInProgress && rii != null) { rii.update(t2 - t1); } } // helper /** * Create a valid ObjectName for this specific combination * * @param mimetype * -- the mimetype for the request * @param templatePath * -- the path to the template (if provided) * @return a valid name which can be converted to an ObjectName */ private String buildMBeanName(String mimetype, String templatePath) { if (templatePath.equals("")) { return MBEAN_PREFIX + ".mime:type=" + mimetype; } else { return MBEAN_PREFIX + ".template:template=" + templatePath; } } private synchronized ServiceRegistration registerReportingService(String mbeanName, String mimeType) { // we need to recheck, as in the meantime it could have been registered already if (!services.containsKey(mbeanName)) { RequestInformationImpl rii = new RequestInformationImpl(mimeType); Dictionary<String, String> props = new Hashtable<String, String>(); props.put("jmx.objectname", mbeanName); log.debug("Registering mbean for " + mbeanName); ServiceRegistration reg = bundleContext.registerService( RequestInformationMBean.class.getName(), rii, props); services.put(mbeanName, reg); return reg; } else { return null; } } }